#######################
#                     #
#  PL0-Idiom Parser   #
#                     #
#######################

###### only for testing ################
# read source_code
source_file = open('C:\\Users\\Nirvash\\workspace\\Python VRISC\\trunk\\PL0\\wirth', 'r') # windows
#source_file = open('wirth', 'r') # linux
source_code = source_file.readlines()
source_file.close()
########################################

### import modules needed for parsing
import pl0_scanner
import pl0_generator
import sys

### define parser class objects

### Identifier object which extends Word and is mainly used to see if an identifier was initialized
class Ident(pl0_scanner.Word):
    def __init__(self, s, tag, i = False, v = False, p = False):
        pl0_scanner.Word.__init__(self, s, tag)
        self.init = i
        self.value = v
        self.procedure = p
        

### define generell parser functions

### Move to next symbol
def move():
    global code
    sym = lex.scan()
    code.append(sym.toString())
    if (sym.toString() == ''): return False
    else: return sym
    
### print out error message
def err(num, sym):
    err_list = {1: "Syntax Error: Program must end with '.'.",
                2: "Type Error: Can only assign number to const.",
                3: "Syntax Error: Missing assign operator '='.",
                4: "Syntax Error: After CONST, VAR or PROCEDURE an identifier must follow.",
                5: "Syntax Error: Missing block statement seperator ';'.",
                6: "Type Error: Identifier not initialized.",
                7: "Type Error: No identifier given.",
                8: "Type Error: Cannot assign expression to a procedure.",
                9: "Type Error: Can only call a procedure.",
                10: "Type Error: Can only READ/PRINT or calculate with Integers.",
                11: "Syntax Error: Missing END or statement seperator ';'.",
                12: "Syntax Error: After IF condition THEN statement must follow.",
                13: "Syntax Error: After WHILE condition DO statement must follow.",
                14: "Syntax Error: Wrong Syntax for statement.",
                15: "Syntax Error: No compare operator.",
                16: "Syntax Error: Missing ')'.",
                17: "Syntax Error: Missing '('."}
    
    line = lex.getline() + 1
    
    mes = str(line) + ": '" + sym.toString() + "' " + err_list[num]
    return sys.exit(mes)   

### define syntax checking functions: 
def program():
    sym = move()
    ins_list.addIns('', 'START', [])
    sym = block(sym)
    ins_list.addIns('', 'HALT', [])
    if (sym.toString() == '.'): return ins_list.list
    else: err(1, sym)

def block(sym):
    if (sym.tag == tag.CONST):
        sym = move()
        while (sym.tag == tag.ID):
            ident = sym
            p = sym.toString()
            sym = move()
            if (sym.toString() == '='):
                sym = move()
                if (sym.tag == tag.NUM):
                    a = sym.toString()
                    sym = move()
                else: err(2, sym)
            else: err(3, sym)
            lex.words[ident.toString()] = Ident(ident.toString(), ident.tag, True, True)
            ins_list.addIns(p, 'DC', [a])
            ins_list.addIns('', 'LDN', [a])
            if (sym.toString() == ','):
                sym = move()
                continue
            else:
                break
        else: err(4, sym)
        if (sym.toString == ";"):
            sym = move()
        else: err(5, sym)
    if (sym.tag == tag.VAR):
        sym = move()
        while (sym.tag == tag.ID):
            lex.words[sym.toString()] = Ident(sym.toString(), sym.tag, True)
            ins_list.addIns(sym.toString(), 'DV', [sym.toString()])
            sym = move()
            if (sym.toString() == ','):
                sym = move()
                continue
            else:
                break
        else: err(4, sym)
        if (sym.toString() == ';'):
            sym = move()
        else: err(5, sym)
    while (sym.tag == tag.PROCEDURE):
        sym = move()
        if (sym.tag == tag.ID):
            ident = sym        
            ins_list.addIns(sym.toString(), '', []) 
            sym = move()
            if (sym.toString() == ';'):
                lex.words[ident.toString()] = Ident(ident.toString(), ident.tag, True, False, True)
                sym = move()
                sym = block(sym)
                if (sym.toString() == ';'):
                    ident.value = True
                    sym = move()
                else: err(5, sym)
            else: err(5, sym)         
        else: err(4, sym)
        ins_list.addIns('', 'RET', [])
    sym = statement(sym)    
    return sym
            

def statement(sym):
    ins_list.stack = []
    if (sym.tag == tag.ID and sym.init == True and sym.procedure == False):
        ins_list.stack.append(sym.toString())
        ident = sym
        sym = move()
        if (sym.toString() == '='):
            ins_list.stack.append(sym.toString())
            sym = move()
            sym = expression(sym)
            ins_list.addIns(ins_list.stack, 'postfix', [])
        else: err(3, sym)
        ident.value = True
    elif (sym.tag == tag.ID and sym.init == False): err(6, sym)
    elif (sym.tag == tag.ID and sym.procedure == True): err(8, sym)
    elif (sym.tag == tag.CALL):
        sym = move()
        if (sym.tag == tag.ID and sym.init == True and sym.procedure == True):
            ins_list.addIns('', 'JSR', [sym.toString()])
            sym = move()
        elif (sym.tag != tag.ID): err(7, sym)
        elif (sym.tag == tag.ID and sym.init == False): err(6, sym)
        elif (sym.tag == tag.ID and sym.procedure == False): err(9, sym)
    elif (sym.tag == tag.READ):
        sym = move()
        if (sym.tag == tag.ID and sym.init == True and sym.procedure == False):
            ins_list.addIns('', 'IN', [1, sym.toString()])
            sym.value == True
            sym = move()
        elif (sym.tag != tag.ID): err(7, sym)
        elif (sym.tag == tag.ID and sym.procedure == True): err(10, sym)
        elif (sym.tag == tag.ID and sym.init == False): err(6, sym)
    elif (sym.tag == tag.PRINT):
        sym = move()
        if (sym.tag == tag.ID and sym.init == True and sym.value == True and sym.procedure == False):
            ins_list.addIns('', 'OUT', [sym.toString(), 2])
            sym = move()
        elif (sym.tag != tag.ID): err(7, sym)
        elif (sym.tag == tag.ID and sym.init == False): err(6, sym)
        elif (sym.tag == tag.ID and sym.procedure == True): err(10, sym)
    elif (sym.tag == tag.BEGIN):
        sym = move()
        sym = statement(sym)
        while (sym.toString() == ';'):
            sym = move()
            sym = statement(sym)
        else: 
            if (sym.tag == tag.END):
                sym = move()
            else: err(11, sym)
    elif (sym.tag == tag.IF):
        ins_list.stack.append(sym.toString())
        sym = move()
        sym = condition(sym)
        ins_list.addIns(ins_list.stack, 'postfix', [])
        if (sym.tag == tag.THEN):
            ins_list.stack.append(sym.toString())
            sym = move()
            sym = statement(sym)
        else: err(12, sym)
    elif (sym.tag == tag.WHILE):
        ins_list.stack.append(sym.toString())
        sym = move()
        sym = condition(sym)
        ins_list.addIns(ins_list.stack, 'postfix', [])
        if (sym.tag == tag.DO):
            ins_list.stack.append(sym.toString())
            sym = move()
            sym = statement(sym)
        else: err(13, sym)
    elif (sym.tag == tag.PASS):
        sym = move()
    else: err(14, sym)
    return sym

def condition(sym):
    if (sym.tag == tag.ODD):
        ins_list.stack.append(sym.toString())
        sym = move()
        sym = expression(sym)
    else: 
        sym = expression(sym)
        if (sym.tag == tag.EQ): 
            ins_list.stack.append(sym.toString())
            sym = move()
            sym = expression(sym)
        elif (sym.tag == tag.NE): 
            ins_list.stack.append(sym.toString())
            sym = move()
            sym = expression(sym)
        elif (sym.tag == tag.GE): 
            ins_list.stack.append(sym.toString())
            sym = move()
            sym = expression(sym)
        elif (sym.tag == tag.LE):
            ins_list.stack.append(sym.toString())
            sym = move()
            sym = expression(sym)
        elif (sym.toString() == '>'):
            ins_list.stack.append(sym.toString())
            sym = move()
            sym = expression(sym)
        elif (sym.toString() == '<'):
            ins_list.stack.append(sym.toString())
            sym = move()
            sym = expression(sym)
        else: err(15, sym)
    return sym        

def expression(sym):
    if (sym.toString() == '+'):
        ins_list.stack.append(sym.toString())
        sym = move()
        sym = term(sym)
    elif (sym.toString() == '-'):
        ins_list.stack.append(sym.toString())
        sym = move()
        sym = term(sym)
    else:
        sym = term(sym)
    while (sym.toString() == '+' or sym.toString() == '-'):
        if (sym.toString() == '+'):
            ins_list.stack.append(sym.toString())
            sym = move()
            sym = term(sym)
        elif (sym.toString() == '-'):
            ins_list.stack.append(sym.toString())
            sym = move()
            sym = term(sym)
    return sym

def term(sym):
    sym = factor(sym)
    while (sym.toString() == '*' or sym.toString() == '/'):
        if (sym.toString() == '*'):
            ins_list.stack.append(sym.toString())
            sym = move()
            sym = factor(sym)
        elif (sym.toString() == '/'):
            ins_list.stack.append(sym.toString())
            sym = move()
            sym = factor(sym)
    return sym    

def factor(sym):
    if (sym.tag == tag.ID and sym.init == True and sym.procedure == False):
        ins_list.stack.append(sym.toString())
        sym = move()
    elif (sym.tag == tag.ID and sym.init == False): err(6)
    elif (sym.tag == tag.ID and sym.procedure == True): err(10)
    elif (sym.tag == tag.NUM):
        ins_list.stack.append(sym.toString())
        sym = move()
    elif (sym.toString() == '('):
        ins_list.stack.append(sym.toString())
        sym = move()
        sym = expression(sym)
        if (sym.toString() == ')'):
            ins_list.stack.append(sym.toString())
            sym = move()
        else: err(16, sym)
    else: err(17, sym)
    return sym


### Parser

### create Lexer object 
tag = pl0_scanner.Tag
lex = pl0_scanner.Lexer()
lex.setCode(source_code)

### create Instruction list created from generator
ins_list = pl0_generator.InstructionList()

sym = ''
code =  []

